package com.rb.common.base;


import com.google.common.collect.Sets;
import com.rb.common.pager.Pagination;
import com.rb.common.sqltpl.UpdateField;
import org.nutz.dao.*;
import org.nutz.dao.entity.Record;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.VarSet;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.nutz.service.EntityService;

import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by walker on 17-7-28.
 */
public class BaseService<T> extends EntityService<T> {

    protected final static int DEFAULT_PAGE_NUMBER = 5;
    @Inject
    private Dao dao;

    public QueryResult page(Condition cnd, Pager pager) {
        List<T> list = query(cnd, pager);
        pager.setRecordCount(count(cnd));
        return new QueryResult(list, pager);
    }

    public T fetch(long id) {
        return this.dao().fetch(this.getEntityClass(), id);
    }

    public T fetch(Class<T> clazz, long id) {
        return this.dao().fetch(clazz, id);
    }


    public T fetch(Class<T> clazz, String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        sql.setEntity(this.dao().getEntity(clazz));
        sql.setCallback(Sqls.callback.entity());
        return this.dao().execute(sql).getObject(clazz);
    }

    public Record fetch(String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        //sql.setEntity(this.dao().getEntity(clazz));
        sql.setCallback(Sqls.callback.record());
        return this.dao().execute(sql).getObject(Record.class);
    }

    public T findOne(Cnd cnd) {
        cnd.limit(1,1);
        List<T> list = query(cnd);
        if (list != null && !list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    public T fetch(String name) {
        return this.dao().fetch(this.getEntityClass(), name);
    }

    public T fetch(Class<T> clazz, String name) {
        return this.dao().fetch(clazz, name);
    }


    public T fetch(Class<T> clazz, Condition cnd) {
        return this.dao().fetch(clazz, cnd);
    }


    public T insert(T t) {
        return _insert(t);
    }

    public int update(T t) {
        return _update(t);
    }

    /**
     * 忽略值为null的字段
     *
     * @param obj
     * @return
     */
    public int updateIgnoreNull(Object obj) {
        return _updateIgnoreNull(obj);
    }

    public int delete(long id) {
        return this.dao().delete(this.getEntityClass(), id);
    }

    public int delete(int id) {
        return this.dao().delete(this.getEntityClass(), id);
    }

    public int delete(String name) {
        return this.dao().delete(this.getEntityClass(), name);
    }


    /**
     * 批量删除
     *
     * @param ids
     */
    public void delete(Integer[] ids) {
        this.dao().clear(getEntityClass(), Cnd.where("id", "in", ids));
    }

    /**
     * 批量删除
     *
     * @param ids
     */
    public void delete(Long[] ids) {
        this.dao().clear(getEntityClass(), Cnd.where("id", "in", ids));
    }

    /**
     * 批量删除
     *
     * @param ids
     */
    public void delete(String[] ids) {
        this.dao().clear(getEntityClass(), Cnd.where("id", "in", ids));
    }


    /**
     * count 总数
     *
     * @param
     * @return 记录数
     */
    public int count(String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        sql.setCallback(Sqls.callback.integer());
        this.dao().execute(sql);
        return (Integer) sql.getResult();
    }

    /**
     * 根据条件统计符合条件的记录数
     *
     * @param cnd 查询条件
     * @return 记录数
     */
    public int count(Class clazz, Condition cnd) {
        return dao().count(clazz, cnd);
    }


    public List<T> list(Condition cnd) {
        return query(cnd);
    }

    public <T> List<T> _query(String queryKey, Map<String, Object> params) {
        return query(getEntityClass(), queryKey, params);
    }

    /**
     * sql文件查询
     *
     * @param clazz
     * @param queryKey 对应的sql文件的查询名 /* selectUserByNote *
     * @param params   查询字段及值
     * @return
     */
    public List query(Class clazz, String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        sql.setEntity(this.dao().getEntity(clazz));
        sql.setCallback(Sqls.callback.entities());
        List<T> list = this.dao().execute(sql).getList(clazz);
        return list;
    }

    /**
     * sql文件查询
     *
     * @param queryKey 对应的sql文件的查询名 /* selectUserByNote *
     * @param params   查询字段及值
     * @return
     */
    public List<Record> query(String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        sql.setCallback(Sqls.callback.records());
        this.dao().execute(sql);
        return sql.getList(Record.class);
    }

    /**
     * 分页查询
     *
     * @param pageNumber
     * @param cnd
     * @return
     */
    public Pagination listPage(Integer pageNumber, Condition cnd) {
        return listPage(pageNumber, DEFAULT_PAGE_NUMBER, cnd);
    }

    /**
     * 分页查询
     *
     * @param pageNumber
     * @param sql
     * @return
     */
    public Pagination listPage(Integer pageNumber, Sql sql) {
        return listPage(pageNumber, DEFAULT_PAGE_NUMBER, sql);
    }

    /**
     * 分页查询
     *
     * @param pageNumber
     * @param tableName
     * @param cnd
     * @return
     */
    public Pagination listPage(Integer pageNumber, String tableName, Condition cnd) {
        return listPage(pageNumber, DEFAULT_PAGE_NUMBER, tableName, cnd);
    }

    /**
     * 分页查询(cnd)
     *
     * @param pageNumber
     * @param pageSize
     * @param cnd
     * @return
     */
    public Pagination listPage(Integer pageNumber, int pageSize, Condition cnd) {
        Pager pager = makePager(pageNumber, pageSize);
        List<T> list = this.dao().query(getEntityClass(), cnd, pager);
        pager.setRecordCount(this.dao().count(getEntityClass(), cnd));
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), list);
    }

    /**
     * 分页查询,获取部分字段(cnd)
     *
     * @param pageNumber
     * @param pageSize
     * @param cnd
     * @param fieldName  支持通配符 ^(a|b)$
     * @return
     */
    public Pagination listPage(Integer pageNumber, int pageSize, Condition cnd, String fieldName) {
        Pager pager = makePager(pageNumber, pageSize);
        List<T> list = Daos.ext(this.dao(), FieldFilter.create(getEntityClass(), fieldName)).query(getEntityClass(), cnd);
        pager.setRecordCount(this.dao().count(getEntityClass(), cnd));
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), list);
    }

    /**
     * 分页查询(tabelName)
     *
     * @param pageNumber
     * @param pageSize
     * @param tableName
     * @param cnd
     * @return
     */
    public Pagination listPage(Integer pageNumber, int pageSize, String tableName, Condition cnd) {
        Pager pager = makePager(pageNumber, pageSize);
        List<Record> list = this.dao().query(tableName, cnd, pager);
        pager.setRecordCount(this.dao().count(tableName, cnd));
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), list);
    }

    /**
     * 分页查询(sql)
     *
     * @param pageNumber
     * @param pageSize
     * @param sql
     * @return
     */
    public Pagination listPage(Integer pageNumber, int pageSize, Sql sql) {
        Pager pager = makePager(pageNumber, pageSize);
        pager.setRecordCount((int) Daos.queryCount(this.dao(), sql));// 记录数需手动设置
        sql.setPager(pager);
        sql.setCallback(Sqls.callback.records());
        dao().execute(sql);
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), sql.getList(Record.class));
    }

    public Pagination listPage(Integer pageNumber, int pageSize, String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        return listPage(pageNumber, pageSize, sql);
    }

    public Pagination listPage(Integer pageNumber, int pageSize, Class<?> clazz, String queryKey, Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        sql.setEntity(this.dao().getEntity(clazz));
        sql.setCallback(Sqls.callback.entities());
        Pager pager = makePager(pageNumber, pageSize);
        pager.setRecordCount((int) Daos.queryCount(this.dao(), sql));// 记录数需手动设置
        sql.setPager(pager);
        dao().execute(sql);
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), sql.getList(clazz));
        // return listPage(pageNumber, pageSize, sql);
    }

    public Pagination listMapPage(Integer pageNumber, int pageSize, String queryKey,  Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        Pager pager = makePager(pageNumber, pageSize);
        pager.setRecordCount((int) Daos.queryCount(this.dao(), sql));// 记录数需手动设置
        sql.setPager(pager);
        sql.setCallback(Sqls.callback.maps());
        dao().execute(sql);
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), sql.getList(Record.class));
    }

    public List<Map> listMap(String queryKey,  Map<String, Object> params) {
        Sql sql = makeSql(queryKey, params);
        sql.setCallback(Sqls.callback.maps());
        dao().execute(sql);
        return sql.getList(Map.class);
    }

    public void execute(String queryKey, Map<String, Object> params){
        Sql sql = makeSql(queryKey, params);
        this.dao().execute(sql);
    }


    /**
     * 默认页码
     *
     * @param pageNumber
     * @return
     */
    protected int getPageNumber(Integer pageNumber) {
        return Lang.isEmpty(pageNumber) ? 1 : pageNumber;
    }

    protected int getPageNumber(NutMap map, String key) {
        return map.getInt(key) > 0 ? map.getInt(key) : 1;
    }

    protected Timestamp now() {
        return new Timestamp(System.currentTimeMillis());
    }

    /**
     * 默认页大小
     *
     * @param pageSize
     * @return
     */
    protected int getPageSize(int pageSize) {
        return pageSize == 0 ? DEFAULT_PAGE_NUMBER : pageSize;
    }

    protected int getPageSize(NutMap map, String key) {
        return map.getInt(key) > 0 ? map.getInt(key) : DEFAULT_PAGE_NUMBER;
    }


    protected Pager makePager(Integer pageNumber, int pageSize) {
        pageNumber = getPageNumber(pageNumber);
        pageSize = getPageSize(pageSize);
        Pager pager = this.dao().createPager(pageNumber, pageSize);
        return pager;
    }

    protected Sql makeSql(String queryKey, Map<String, Object> params) {
        Sql sqlDB = dao().sqls().create(queryKey);
        VarSet vars = sqlDB.params();
        if (null != params && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                vars.set(entry.getKey(), entry.getValue());
            }
        }
        return sqlDB;
    }

    public void update(String id, UpdateField... updateFields){
        if(updateFields.length == 0){
            return;
        }
        Chain chain = Chain.make("id", id);
        Set<String> lockFields = Sets.newHashSet( "createTime");//不允许更新的字段
        for (UpdateField updateField : updateFields){
            if(lockFields.contains(updateField.getProperty())){
                continue;
            }
            chain.add(updateField.getProperty(), updateField.getValue());
        }
        update(chain, Cnd.where("id", "=", id));
    }

    public void update(NutMap params){
        String id = params.getString("id");
        Chain chain = Chain.make("id", id);
        Field[] fields = getEntity().getMirror().getFields();
        Set<String> beanFields = Sets.newHashSet();
        for(Field field : fields){
            beanFields.add(field.getName());
        }

        Set<String> lockFields = Sets.newHashSet("createTime");//不允许更新的字段

        for (Map.Entry<String, Object> entry : params.entrySet()){
            if(lockFields.contains(entry.getKey())){
                continue;
            }
            if(beanFields.size() > 0 && !beanFields.contains(entry.getKey())){
                continue;
            }
            chain.add(entry.getKey(), entry.getValue());
        }
        update(chain, Cnd.where("id", "=", id));
    }
}
